package com.huayu.languo;

import lombok.*;
import lombok.experimental.Accessors;

import java.lang.reflect.Field;
import java.lang.reflect.Modifier;
import java.lang.reflect.ParameterizedType;
import java.lang.reflect.Type;
import java.sql.*;
import java.util.Date;
import java.util.*;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
import java.util.stream.Collectors;


/**
 * @author 刘雄康
 * @version v1.0
 * @description  根据类信息，生成少量测试实体数据。 特殊要求字段，可等生成后再次赋值。
 *               由于使用到反射，大批量数据生成待优化
 *               根据 validate 标签 |  数据库字段信息，注入 符合规范 的值
 * @date 2019 年 05月 20日
 */
public class InjectionTestObjHelper {


    /**
     * hash map 大小
     */
    private static final int CONFIG_SIZE = 16;

    /**
     * 注入器 配置
     */
    public InjectionConfig injectionConfig;

    /**
     * mysql 表信息定义
     */
    private Map<String, Map<String, FieldInfo>> tableFieldInfo = new HashMap<>(CONFIG_SIZE);

    public InjectionTestObjHelper() {
        this(null);
    }


    public InjectionTestObjHelper(InjectionConfig configs) {
        if (null == configs) {
            injectionConfig = new InjectionConfig();
        }
        this.injectionConfig = configs;
    }

    public static String toLowCaseStr(String data) {
        if (null == data) {
            return null;
        }
        return data.replace("_" , "").toLowerCase();
    }

    public static String toStr(Object data) {
        if (null == data) {
            return "" ;
        }
        return data.toString().trim();
    }

    private static boolean notEmpty(String... data) {
        if (null == data) {
            return false;
        }
        for (String tp : data) {
            if (null == tp || "".equals(tp.trim())) {
                return false;
            }
        }
        return true;
    }

    @SneakyThrows
    public void initAllTableInfo() {
        Set<String> tableNameSet = new HashSet<>();
        try (PreparedStatement preparedStatementTable = injectionConfig.getConnection().prepareStatement(injectionConfig.getDbQuery().tablesSql());
             ResultSet resultsTable = preparedStatementTable.executeQuery()) {
            while (resultsTable.next()) {
                String tableName = resultsTable.getString(injectionConfig.getDbQuery().tableName());
                String lowCaseTableName = toLowCaseStr(tableName);
                Map<String, FieldInfo> fieldInfoMap = getTableInfo(tableName);
                tableFieldInfo.put(lowCaseTableName, fieldInfoMap);
                tableNameSet.add(lowCaseTableName);
            }
        }
        if (tableNameSet.size() != tableFieldInfo.size()) {
            throw new RuntimeException("  表名 不规范！！ ");
        }
    }

    @SneakyThrows
    public Map<String, FieldInfo> getTableInfo(String tableName) {
        if (null == tableName) {
            return null;
        }
        String lowCaseTableName = toLowCaseStr(tableName);
        if (tableFieldInfo.containsKey(lowCaseTableName)) {
            return tableFieldInfo.get(lowCaseTableName);
        }

        MySqlQuery dbQuery = injectionConfig.getDbQuery();
        String tableFieldsSql = String.format(dbQuery.tableFieldsSql(), tableName);
        Map<String, FieldInfo> fieldInfoMap = new HashMap<>(CONFIG_SIZE);
        try (PreparedStatement preparedStatementField = injectionConfig.getConnection().prepareStatement(tableFieldsSql);
             ResultSet resultsField = preparedStatementField.executeQuery()) {
            List<String> fieldList = new ArrayList<>();
            while (resultsField.next()) {
                String fieldName = resultsField.getString(dbQuery.fieldName());
                String lowCaseFieldName = toLowCaseStr(fieldName);
                FieldInfo fieldInfo = new FieldInfo();
                fieldInfo.setLowerCaseFieldName(lowCaseFieldName);
                fieldInfo.setFieldName(fieldName);
                fieldInfo.setTableName(tableName);
                fieldInfo.setFieldType(resultsField.getString(dbQuery.fieldType()));
                fieldInfo.setFieldComment(resultsField.getString(dbQuery.fieldComment()));
                fieldInfo.setFieldExtra(resultsField.getString(dbQuery.fieldExtra()));
                fieldInfo.setFieldNull(resultsField.getString(dbQuery.fieldNull()));
                fieldInfo.setFieldDefault(resultsField.getString(dbQuery.fieldDefault()));
                fieldInfo.setFieldKey(resultsField.getString(dbQuery.fieldKey()));

                fieldInfoMap.put(lowCaseFieldName, fieldInfo);
                fieldList.add(lowCaseFieldName);
            }
            if (fieldList.size() != fieldInfoMap.size()) {
                throw new RuntimeException(tableName + "  字段名 不规范！！ ");
            }
        }
        tableFieldInfo.put(lowCaseTableName, fieldInfoMap);
        return fieldInfoMap;
    }

    @SneakyThrows
    public <T extends Object> T injection(Class<T> clazz) {
        if (null == clazz) {
            return null;
        }
        // 如果是字符串等基本类型，则直接生成值，否则依据类型继续注入
        BaseResult baseDefaultValue = getBaseDefaultValue(clazz, injectionConfig.getStrFlagDefault(), "");
        if (null != baseDefaultValue.getData()) {
            return (T) baseDefaultValue.getData();
        }
        return injection(clazz.newInstance());
    }

    public <T extends Object> T injection(T obj) {
        if (null == obj) {
            return null;
        }
        // 如果是字符串等基本类型，则直接生成值，否则依据类型继续注入
        BaseResult baseDefaultValue = getBaseDefaultValue(obj, injectionConfig.getStrFlagDefault(), "");
        if (null != baseDefaultValue.getData()) {
            return (T) baseDefaultValue.getData();
        }
        return injection(obj, "" , "");
    }

    @SneakyThrows
    private <T extends Object> T injection(T obj, String parentBeanFieldName, String parentBeanClassName) {
        if (null == obj) {
            return null;
        }
        // 如果是字符串等基本类型，则直接生成值，否则依据类型继续注入
        BaseResult baseDefaultValue0 = getBaseDefaultValue(obj, injectionConfig.getStrFlagDefault(), parentBeanClassName);
        if (null != baseDefaultValue0.getData()) {
            return (T) baseDefaultValue0.getData();
        }
        Class<?> aClass = obj.getClass();
        String className = aClass.getName().substring(aClass.getName().lastIndexOf(".") + 1);
        List<Field> declaredFields = getDeclaredFields(aClass);
        for (Field declaredField : declaredFields) {
            Field modifiersField = Field.class.getDeclaredField("modifiers");
            modifiersField.setAccessible(true);
            modifiersField.setInt(declaredField, declaredField.getModifiers() & ~Modifier.FINAL);

            Class<?> type = declaredField.getType();
            declaredField.setAccessible(true);
            String fieldName = declaredField.getName();
            // 过滤字段
            if (injectionConfig.getNullId() && "id".equalsIgnoreCase(fieldName)) {
                continue;
            }
            if ((!notEmpty(parentBeanFieldName) && injectionConfig.getNullFields().contains(fieldName))
                    || injectionConfig.getNullFields().contains(toStr(parentBeanFieldName).concat(".").concat(fieldName))) {
                continue;
            }

            BaseResult baseDefaultValue = getBaseDefaultValue(type, fieldName, className);
            if (baseDefaultValue.getFilter()) {
                continue;
            } else if (null != baseDefaultValue.getData()) {
                declaredField.set(obj, baseDefaultValue.getData());
            } else if (null != (baseDefaultValue = getCollectionValue(type, declaredField, className)).getData()) {
                declaredField.set(obj, baseDefaultValue.getData());
            } else if (null != (baseDefaultValue = getEnumsValue(type, declaredField)).getData()) {
                declaredField.set(obj, baseDefaultValue.getData());
            } else {
                declaredField.set(obj, injection(type.newInstance(), fieldName, className));
            }
        }
        return obj;
    }

    public <T> List<Field> getDeclaredFields(Class<T> aClass) {
        List<Field> objects = new ArrayList<>();
        objects.addAll(Arrays.asList(aClass.getDeclaredFields()));

        Class<? super T> superclass = aClass.getSuperclass();
        if (superclass instanceof Object && superclass.getSuperclass() == null && superclass.getName().equals("java.lang.Object")) {
            return objects;
        }
        objects.addAll(getDeclaredFields(superclass));
        return objects;
    }

    private int getConstraint(String type) {
        Pattern compile = Pattern.compile(".*\\((\\d*)\\).*");
        Matcher matcher = compile.matcher(type);
        if (matcher.matches()) {
            String group = matcher.group(1);
            return Integer.valueOf(group);
        }
        return -1;
    }


    private String convertBeanNameToTableName(String beanName) {
        StringBuffer sb = new StringBuffer();
        Pattern p = Pattern.compile("[A-Z]");
        Matcher m = p.matcher(beanName);
        while (m.find()) {
            m.appendReplacement(sb, "_" + m.group().toLowerCase());
        }
        m.appendTail(sb);
        return sb.toString().replaceFirst("^_" , "");
    }


    public List<String> getEnumsConstraint(String comment, int constraint, int type) {
        String commentEnumsConstraintPattern = injectionConfig.getCommentEnumsConstraintPattern();
        Set<String> enumsConstraint = new HashSet<>();
        if (notEmpty(commentEnumsConstraintPattern)) {
            Pattern compile = Pattern.compile(commentEnumsConstraintPattern);
            Matcher matcher = compile.matcher(comment);
            while (matcher.find()) {
                enumsConstraint.add(matcher.group());
            }
            // TODO 新增个java 8 后处理方法参数，  供调用后处理结果字符
        } else {
            if (type == 1) {
                enumsConstraint = new HashSet<>(
                        Arrays.asList(comment.replaceAll("[^-0-9]" , ",").replaceAll("-*," , ",").replaceAll(",{2,}" , ",").replaceAll("^,*|,*$|-*$" , "").split(",")));
            } else if (type == 2) {
                Pattern compile = Pattern.compile("-?[0-9]+|[a-zA-Z]");
                Matcher matcher = compile.matcher(comment);
                while (matcher.find()) {
                    enumsConstraint.add(matcher.group());
                }
            } else if (type == 3) {
                enumsConstraint = new HashSet<>(
                        Arrays.asList(comment.replaceAll("[^a-zA-Z-0-9]" , ",").replaceAll("-*," , ",").replaceAll(",{2,}" , ",").replaceAll("^,*|,*$|-*$" , "").split(",")));
            }
        }
        return enumsConstraint.stream().filter(k -> null != k && k.trim().length() > 0 && k.trim().length() <= constraint).collect(Collectors.toList());
    }

    /**
     * 获取 基本类型的 值
     */
    @SneakyThrows
    private BaseResult getBaseDefaultValue(Object object, String filedName, String parentBeanClassName) {
        FieldInfo fieldInfo = null;
        boolean useDataSourceConstraint = false;
        if (injectionConfig.getUseDataSourceConstraint() && notEmpty(parentBeanClassName)) {
            String tableName = injectionConfig.getBeanClassOfTableName().get(toLowCaseStr(parentBeanClassName));
            if (!notEmpty(tableName)) {
                tableName = convertBeanNameToTableName(injectionConfig.getTablePrefix().concat(parentBeanClassName));
            }
            Map<String, FieldInfo> tableInfo = getTableInfo(tableName);
            if (null != tableInfo) {
                fieldInfo = tableInfo.get(toLowCaseStr(filedName));
                useDataSourceConstraint = fieldInfo != null;
            }
        }

        // 过滤 数据库 主键 、 可以为null 的字段
        if (useDataSourceConstraint) {
            String fieldNull = fieldInfo.getFieldNull();
            String fieldExtra = fieldInfo.getFieldExtra();
            String fieldKey = fieldInfo.getFieldKey();
            if (injectionConfig.getNullCanNull() && "yes".equals(toStr(fieldNull).toLowerCase())) {
                return BaseResult.builder().filter(true).data(null).build();
            } else if (injectionConfig.getNullPriKey() && "pri".equals(toStr(fieldKey).toLowerCase()) && "auto_increment".equals(toStr(fieldExtra).toLowerCase())) {
                return BaseResult.builder().filter(true).data(null).build();
            }
        }


        Class className = object instanceof Class ? ((Class) object) : object.getClass();
        Object data = null;
        if (className.equals(Integer.class) || className.equals(Short.class)) {
            Integer numberValRandomRange = injectionConfig.getNumberValRandomRange();
            if (useDataSourceConstraint) {
                String fieldType = fieldInfo.getFieldType();
                String fieldComment = fieldInfo.getFieldComment();
                String fieldNull = fieldInfo.getFieldNull();
                //mysql数字类型的长度 是 显示宽度，不限制值大小。int最大有10位。 int(2)只是显示2位。 int(2) 显示应该是 0-99，取值应该是0-4 294 967 295
                int constraint = getConstraint(fieldType);
                int constraintRange = constraint > 0 ? Double.valueOf(Math.pow(10, constraint)).intValue() : constraint;
                // 只有 字段长度小于5时，触发枚举值匹配
                if (0 < constraint && constraint <= 5) {
                    List<String> enumsConstraint = getEnumsConstraint(fieldComment, constraint, 1);
                    if (null != enumsConstraint && enumsConstraint.size() > 0) {
                        int i = new Random().nextInt(enumsConstraint.size());
                        // 枚举值可以为null的，在没过滤为null的值的时候，有一定概率为null
                        if (notEmpty(fieldNull) && "yes".equals(toLowCaseStr(fieldNull)) && i == 1) {
                            return BaseResult.builder().filter(true).data(null).build();
                        }
                        data = Integer.valueOf(enumsConstraint.get(i));
                    }
                }
                if (0 < constraint && null == data) {
                    numberValRandomRange = numberValRandomRange > constraintRange ? constraintRange : numberValRandomRange;
                }
            }
            if (null == data) {
                data = Math.abs(new Random().nextInt(numberValRandomRange));
            }
        } else if (className.equals(Long.class)) {
            Integer numberValRandomRange = injectionConfig.getNumberValRandomRange();
            if (useDataSourceConstraint) {
                String fieldType = fieldInfo.getFieldType();
                int constraint = getConstraint(fieldType);
                int constraintRange = constraint > 0 ? Double.valueOf(Math.pow(10, constraint)).intValue() : constraint;
                numberValRandomRange = numberValRandomRange > constraintRange && constraintRange > 0 ? constraintRange : numberValRandomRange;
            }
            if (null == data) {
                data = Long.valueOf(Math.abs(new Random().nextInt(numberValRandomRange)));
            }
        } else if (className.equals(String.class)) {
            if (!injectionConfig.getUseFiledName()) {
                filedName = injectionConfig.getStrFlagDefault();
            }
            int strNumberValRandomRange = injectionConfig.getStrNumberValRandomRange();
            int constraint = 0;
            if (useDataSourceConstraint) {
                String fieldType = fieldInfo.getFieldType();
                String fieldComment = fieldInfo.getFieldComment();
                String fieldNull = fieldInfo.getFieldNull();
                constraint = getConstraint(fieldType);
                int constraintRange = constraint > 0 ? Double.valueOf(Math.pow(10, constraint)).intValue() : constraint;
                // 只有 字段 长度小于5时，触发枚举值匹配
                if (0 < constraint && constraint <= 5) {
                    List<String> enumsConstraint = getEnumsConstraint(fieldComment, constraint, 3);
                    if (null != enumsConstraint && enumsConstraint.size() > 0) {
                        int i = new Random().nextInt(enumsConstraint.size());
                        // 枚举值可以为null的，在没过滤为null的值的时候，有一定概率为null
                        if (notEmpty(fieldNull) && "yes".equals(toLowCaseStr(fieldNull)) && i == 1) {
                            return BaseResult.builder().filter(true).data(null).build();
                        }
                        data = enumsConstraint.get(i);
                    }
                }
                if (0 < constraint && null == data) {
                    strNumberValRandomRange = strNumberValRandomRange > constraintRange ? constraintRange : strNumberValRandomRange;
                }
            }

            String dataTemp = "" ;
            String strPre = injectionConfig.getStrValPre();
            String strFlagConcat = injectionConfig.getStrFlagConcat();

            String randomNumber = String.valueOf(Math.abs(new Random().nextInt(strNumberValRandomRange)));
            if (notEmpty(filedName)) {
                dataTemp = strPre.concat(strFlagConcat).concat(filedName).concat(strFlagConcat).concat(randomNumber);
            } else {
                dataTemp = strPre.concat(strFlagConcat).concat(randomNumber);
            }
            if (constraint > 0 && dataTemp.length() > constraint) {
                if (constraint >= 10 && String.valueOf(strNumberValRandomRange).length() <= 5) {
                    dataTemp = "sub_" + dataTemp.substring(dataTemp.length() - constraint - 4);
                    dataTemp = dataTemp.replace("__" , "_");
                } else {
                    dataTemp = dataTemp.substring(dataTemp.length() - constraint);
                }
            }
            data = dataTemp;
        } else if (className.equals(Byte.class)) {
            byte[] bytes = new byte[1];
            new Random().nextBytes(bytes);
            data = Math.abs(bytes[0]);
        } else if (className.equals(Double.class)) {
            data = new Random().nextDouble();
        } else if (className.equals(Float.class)) {
            data = new Random().nextFloat();
        } else if (className.equals(Character.class)) {
            data = '\u0000';
        } else if (className.equals(Boolean.class)) {
            data = new Random().nextInt(injectionConfig.getRandomBooleanF()) % injectionConfig.getRandomBooleanE() == 0;
        } else if (className.equals(Date.class)) {
            data = new Date();
        } else {
            if (object instanceof Class && ((Class) object).isPrimitive()) {
                data = Math.abs(new Random().nextInt(injectionConfig.getNumberValRandomRange()));
            }
        }
        return BaseResult.builder().filter(false).data(data).build();
    }

    /**
     * 获取 集合 类型的 值
     */
    @SneakyThrows
    public BaseResult getCollectionValue(Object object, Field declaredField, String parentBeanClassName) {
        Class className = object instanceof Class ? ((Class) object) : object.getClass();
        Object data;
        if (className.equals(List.class) || className.equals(Set.class)) {
            // 如果是List类型，得到其 Generic 的类型
            Type genericType = declaredField.getGenericType();
            if (genericType == null) {
                System.err.println("---- genericType == null ----");
            }
            // 如果是泛型参数的类型
            Class<?> genericClazz = null;
            if (genericType instanceof ParameterizedType) {
                try {
                    ParameterizedType pt = (ParameterizedType) genericType;
                    //得到泛型里的class类型对象
                    genericClazz = (Class<?>) pt.getActualTypeArguments()[0];
                } catch (Exception e) {
                    e.printStackTrace();
                }
            }
            if (genericClazz == null) {
                genericClazz = String.class;
            }
            Collection<Object> objects = new ArrayList<>();
            if (className.equals(Set.class)) {
                objects = new HashSet<>();
            }
            for (int i = 0; i < injectionConfig.getGenerateCollSize(); i++) {
//                objects.add(injection(genericClazz));
                objects.add(injection(genericClazz.newInstance(), declaredField.getName(), parentBeanClassName));
            }
            data = objects;
        } else if (className.equals(Map.class)) {
            // TODO
            data = Collections.emptyMap();
        } else {
            // TODO  another type to do
            data = null;
        }
        return BaseResult.builder().filter(false).data(data).build();
    }


    /**
     * 获取 集合 类型的 值
     */
    @SneakyThrows
    public BaseResult getEnumsValue(Object object, Field declaredField) {
        Class className = object instanceof Class ? ((Class) object) : object.getClass();
        Object data = null;
        if (className.isEnum()) {
            // 得到所有枚举常量
            Object[] objects = className.getEnumConstants();
            data = objects[new Random().nextInt(objects.length)];
        }
        return BaseResult.builder().filter(false).data(data).build();
    }


    @Data
    @Builder
    static class BaseResult {

        private Boolean filter;

        private Object data;
    }

    /**
     * MySql 表数据查询
     */
    static class MySqlQuery {

        public String tablesSql() {
            return "show table status" ;
        }

        public String tableFieldsSql() {
            return "show full fields from `%s`" ;
        }

        public String tableName() {
            return "NAME" ;
        }

        public String tableComment() {
            return "COMMENT" ;
        }

        public String fieldName() {
            return "FIELD" ;
        }

        public String fieldType() {
            return "TYPE" ;
        }

        /**
         * 是否是行内（0-行内，1-行外）
         * 推荐人生成方式(1-随机生成，0-其他)
         * (字典数据类型)01-业务考核，02-投放主体，03-付费方式
         * 0未推送，1已推送
         * 活动场景类型（1：下线开卡并资产提升，2：新下载，3：生活缴费， 4：见者有份，5：私行，6：基客）
         * 表类型(1、推荐信息表 2、资格结果表 3、规则结果表)
         */
        public String fieldComment() {
            return "COMMENT" ;
        }

        public String fieldKey() {
            return "KEY" ;
        }

        public String fieldDefault() {
            return "DEFAULT" ;
        }

        // auto_increment
        public String fieldExtra() {
            return "EXTRA" ;
        }

        public String fieldNull() {
            return "NULL" ;
        }

        public boolean isKeyIdentity(ResultSet results) throws SQLException {
            return "auto_increment".equals(results.getString(fieldExtra()));
        }
    }


    @Data
    @NoArgsConstructor
    @AllArgsConstructor
    @EqualsAndHashCode(callSuper = false)
    @Accessors(chain = true)
    static class FieldInfo {

        private String lowerCaseFieldName;

        private String fieldName;

        private String tableName;

        private String fieldType;

        private String fieldComment;

        private String fieldExtra;

        private String fieldNull;

        private String fieldDefault;

        private String fieldKey;
    }

    @Data
    @EqualsAndHashCode(callSuper = false)
    @Accessors(chain = true)
    public static class InjectionConfig {

        /**
         * 默认字符串
         */
        private static final String DEFAULT_STR_VALUE = "" ;

        /**
         * 字符串值 的前缀
         */
        private String strValPre;

        /**
         * 纯粹字符串 的 filed名
         */
        private String strFlagDefault = "str" ;

        /**
         * 对象下string 字段填充 值的 拼接符号
         */
        private String strFlagConcat;

        /**
         * str的值,后面拼接随机数 取值范围
         */
        private Integer strNumberValRandomRange = 10000;

        /**
         * 对象下string 字段填充是否 包含字段名
         */
        private Boolean useFiledName = true;

        /**
         * true ,为id 的字段 不赋值。大小不敏感
         */
        private Boolean nullId = false;

        /**
         * nullId 优先
         * true ,数据库 为 自增主键 的字段 不赋值。大小不敏感
         */
        private Boolean nullPriKey = false;

        /**
         * true ,数据库 可以为 null 的 不赋值。大小不敏感
         */
        private Boolean nullCanNull = false;

        /**
         * 不赋值的字段名。  大小敏感
         * 比如 school 学校类 里有字段 schoolId ,有 clazz 班级对象。 clazz 班级 里也有 schoolId。
         * 此时想过滤字段 schoolId 。   如果想过滤 school 里的  schoolId ，添加 schoolId，就行， 如是 clazz 则添加 clazz.schoolId
         */
        private List<String> nullFields;


        /**
         * list,set,map 等 生成数据的 size
         */
        private Integer generateCollSize = 3;

        /**
         * boolean 类型值 生成true/false 的除数 。可以控制 true/false 概率
         * new Random().nextInt(randomBooleanF) % randomBooleanE == 0
         */
        private Integer randomBooleanF = 3;

        /**
         * boolean 类型值 生成true/false 的被除数 。可以控制 true/false 概率
         * new Random().nextInt(randomBooleanF) % randomBooleanE == 0
         */
        private Integer randomBooleanE = 2;

        /**
         * 数值类型的值 随机数取值范围
         */
        private Integer numberValRandomRange = 10000;


        /**
         * 表名前缀
         */
        private String tablePrefix;

        /**
         * 字段对象class名-of-表名 映射   class将转为小写，表面名将转为小写，且去掉下划线
         * 如果想 使用数据库  约束字段值, 但又不想 配置 class 表映射时。
         * <p>
         * 会使用默认匹配方式： 先将 表前缀 + 对象类型名   转为 小写，去掉下划线。   将数据库表名  转为小写 ,去掉下划线
         * 有匹配到表字段约束信息，则按约束 注入value 值， 否则  按 不需使用 数据库 约束注入value值
         */
        private Map<String, String> beanClassOfTableName;

        /**
         * url 配置
         */
        private String dataSourceUrl;

        /**
         * dataSourceUserName
         */
        private String dataSourceUserName;

        /**
         * dataSourcePassWord
         */
        private String dataSourcePassWord;

        /**
         * 可以根据 dataSourceUrl 判断 是使用jdbcUrl，是则是  mysql 驱动。
         */
        @Setter(AccessLevel.PRIVATE)
        private String dataSourceDiverClassName;


        /**
         * 是否使用 数据库 约束字段值
         */
        @Setter(AccessLevel.PRIVATE)
        private Boolean useDataSourceConstraint = false;

        /**
         * 从数据库 字段 注释里获取 枚举值的正则，不填使用默认模版
         */
        private String commentEnumsConstraintPattern;


        /**
         * 可以根据 jdbc Url 判断 是使用   mysql 数据字典
         * 暂时只有 MySqlQuery,没有基类
         */
        @Setter(AccessLevel.PRIVATE)
        private MySqlQuery dbQuery;

        /**
         * data source 连接
         */
        @Setter(AccessLevel.PRIVATE)
        private Connection connection;


        public InjectionConfig() {
            this(null);
        }

        public InjectionConfig(Map<String, Object> configs) {
            if (null == configs) {
                configs = new HashMap<>(CONFIG_SIZE);
            }
            try {
                strValPre = toStr(configs.getOrDefault("strValPre" , DEFAULT_STR_VALUE));
                strFlagDefault = toStr(configs.getOrDefault("strFlagDefault" , strFlagDefault));
                strFlagConcat = toStr(configs.getOrDefault("strFlagConcat" , DEFAULT_STR_VALUE));
                strNumberValRandomRange = (int) configs.getOrDefault("strNumberValRandomRange" , strNumberValRandomRange);
                useFiledName = (boolean) configs.getOrDefault("useFiledName" , useFiledName);

                nullId = (Boolean) configs.getOrDefault("nullId" , nullId);
                nullPriKey = (Boolean) configs.getOrDefault("nullPriKey" , nullPriKey);
                nullCanNull = (Boolean) configs.getOrDefault("nullCanNull" , nullCanNull);
                nullFields = (List<String>) configs.getOrDefault("nullFields" , Collections.emptyList());

                generateCollSize = (int) configs.getOrDefault("generateCollSize" , generateCollSize);
                randomBooleanF = (int) configs.getOrDefault("randomBooleanF" , randomBooleanF);
                randomBooleanE = (int) configs.getOrDefault("randomBooleanE" , randomBooleanE);
                numberValRandomRange = (int) configs.getOrDefault("numberValRandomRange" , numberValRandomRange);

                tablePrefix = toStr(configs.getOrDefault("tablePrefix" , DEFAULT_STR_VALUE));
                commentEnumsConstraintPattern = toStr(configs.getOrDefault("commentEnumsConstraintPattern" , DEFAULT_STR_VALUE));

                Map<String, String> beanOfTableNameTemp = (Map<String, String>) configs.getOrDefault("beanClassOfTableName" , Collections.emptyMap());
                beanClassOfTableName = new HashMap<>(CONFIG_SIZE);
                for (Map.Entry<String, String> entry : beanOfTableNameTemp.entrySet()) {
                    beanClassOfTableName.put(toLowCaseStr(entry.getKey()), toLowCaseStr(entry.getValue()));
                }
                if (beanOfTableNameTemp.size() != beanClassOfTableName.size()) {
                    throw new RuntimeException(" 对象-表名 不规范！！ ");
                }

                dataSourceUrl = toStr(configs.getOrDefault("dataSourceUrl" , DEFAULT_STR_VALUE));
                dataSourceUserName = toStr(configs.getOrDefault("dataSourceUserName" , DEFAULT_STR_VALUE));
                dataSourcePassWord = toStr(configs.getOrDefault("dataSourcePassWord" , DEFAULT_STR_VALUE));
                checkUseDataSource();
            } catch (Exception e) {
                System.err.println(" config 字段配置 错误！！！！ ");
                e.printStackTrace();
            }
        }

        @SneakyThrows
        private void checkUseDataSource() {
            if (notEmpty(dataSourceUrl, dataSourceUserName, dataSourcePassWord)) {
                if (dataSourceUrl.startsWith("jdbc:mysql:")) {
                    dataSourceDiverClassName = "com.mysql.jdbc.Driver" ;
                    dbQuery = new MySqlQuery();
                } else {
                    // TODO 其他数据库类型
                }
                useDataSourceConstraint = true;
                Class.forName(dataSourceDiverClassName);
                connection = DriverManager.getConnection(dataSourceUrl, dataSourceUserName, dataSourcePassWord);
            }
        }

        public InjectionConfig setDataSourceUrl(String dataSourceUrl) {
            this.dataSourceUrl = dataSourceUrl;
            checkUseDataSource();
            return this;
        }

        public InjectionConfig setDataSourceUserName(String dataSourceUserName) {
            this.dataSourceUserName = dataSourceUserName;
            checkUseDataSource();
            return this;
        }

        public InjectionConfig setDataSourcePassWord(String dataSourcePassWord) {
            this.dataSourcePassWord = dataSourcePassWord;
            checkUseDataSource();
            return this;
        }
    }
}
